package com.service.atf.base.core;

import com.service.atf.base.exception.TFException;
import com.service.atf.base.model.BaseHeader;
import com.service.atf.base.type.*;
import com.service.atf.base.utils.ATFConstants;
import com.service.atf.base.utils.ATFLogger;
import com.luotao.devp.base.db.Pager;
import com.luotao.devp.base.utils.ByteUtil;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.ReentrantLock;

/**
 * =============================================================================<br>
 * 类型名称: TFWriter<br>
 * 类型版本: V1.0<br>
 * 类型描述: 文件池类操作基础类 <br>
 * 类型作者: luotao <br>
 * 创建日期: 2011-02-23 <br>
 * 类型备注: <br>
 * =============================================================================<br>
 * 修改日期 修改前版本 修改后版本 修改人 备注<br>
 * =============================================================================
 */
@SuppressWarnings("unused")
public class TFWriter {
    private ReentrantLock wtlock = new ReentrantLock();

    /**
     * 是否关闭
     */
    private boolean beclosed;

    private String currentFilePath;
    // 操作文件句柄
    private FileChannel channel;
    // 交换文件文件访问句柄
    private RandomAccessFile accfile;
    // 头部写映射
    protected BuffWriterHead buffWriterHead;
    // 头部读映射
    protected BuffReaderHead buffReaderHead;
    // checkpoint
    protected MappedByteBuffer buffCheckpoint;

    public String nextFilePath;

    public String getCurrentFilePath() {
        return currentFilePath;
    }

    public String getCurrentFileName() {
        return currentFilePath.substring(currentFilePath.length() - 10 - ATFConstants.TF_PATH_SUFFIX.length(), currentFilePath.length() - ATFConstants.TF_PATH_SUFFIX.length());
    }

    private boolean isCreate;

    public void setPager(Pager pager) {
        this.pager = pager;
    }

    public Pager getPager() {
        return pager;
    }

    public MappedByteBuffer getBuffBody() {
        return buffBody;
    }

    // 写入的总记录数
    private long writedTotal;

    public long getWritedTotal() {
        return writedTotal;
    }

    public void resetWritedTotal() {
        writedTotal = 0;
    }

    //
    private String prevFilePath;

    public void setPrevFilePath(String prevFilePath) {
        this.prevFilePath = prevFilePath;
    }

    /**
     * 文件体部映射
     */
    protected transient MappedByteBuffer buffBody;
    private int memyIndex;
    Pager pager;

    public TFWriter(String filePath, int pageSize, int totalCount, int readIndex, boolean isCreate) {
        this.isCreate = isCreate;
        if (totalCount % pageSize != 0) {
            throw new RuntimeException("内存块大小不是" + pageSize + "的整数倍,totalCount : "+totalCount);
        }
        Pager pager = new Pager(pageSize);
        pager.setTotalCount(totalCount);
        this.pager = pager;
        currentFilePath = filePath;
        createFileInfo(filePath, isCreate);
        buffReaderHead = new BuffReaderHead(channel, readIndex);
    }

    public int initHeader() {
        if (isCreate) {
            return createHeader();
        } else {
            return loadHeader();
        }
    }

    public void initBody(int leftSize) {
        if (isCreate) {
            createBody();
        } else {
            loadBody(leftSize);
        }
    }

    public void initBody() {
        createBody();
    }

    private void createFileInfo(String filePath, boolean isCreate) {
        try {
            if (isCreate) {
                File pfile = new File(filePath);
                if (pfile.exists()) {
                    pfile.delete();
                }
                boolean success = pfile.createNewFile();
                if (!success) {
                    throw new RuntimeException("创建TF文件失败");
                }
            }
            accfile = new RandomAccessFile(currentFilePath, "rw");
            String osName = System.getProperty("os.name");
            channel = accfile.getChannel();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private int createHeader() {
        buffWriterHead = new BuffWriterHead(channel, 0);
        buffWriterHead.write(GlobalDefine.TF_DEFAULT_HEADER_VALUE);
        // 写入REPLY指针，DB1指针，上报指针
        for (int i = 1; i < 5; i++) {
            BuffWriterHead buffHead = new BuffWriterHead(channel, i);
            buffHead.write(GlobalDefine.TF_DEFAULT_HEADER_VALUE);
        }
        return pager.getPageSize();
    }

    private void createBody() {
        createTFBody();
    }

    /**
     * @return
     * @throws Exception 返回TF文件剩余可写数量
     */
    private int loadHeader() {
        // 创建头部写缓存
        buffWriterHead = new BuffWriterHead(channel, 0);
        // 找到最后一条写入数据的长度（包括该记录的长度字段）
        int adder = buffWriterHead.read();
        if (adder == GlobalDefine.TF_DEFAULT_HEADER_VALUE) {
            return 0;
        }
        BuffReaderBodyLen buffReaderBodyLen = new BuffReaderBodyLen(channel, adder);
        // 计算TF文件中的实际可写数量
        int lastLen = buffReaderBodyLen.read() + adder + GlobalDefine.TF_RECORD_LEN_BYTE;
        return lastLen;
        /*
        * Long leftSize=pager.getPageSize()*pager.getTotalPages()-lastLen; if(leftSize<=0){ return
        * leftSize.intValue(); } return leftSize.intValue();
        */
    }

    private void loadBody(int lastLen) {

        int pagerNo = lastLen / pager.getPageSize() + 1;
        pager.setPageNo(pagerNo);
        createTFBody();
        int start = lastLen - (pager.getFirst() - 1);
        buffBody.position(start);
    }

    public byte[] getTFStartData() {
        byte[] data = new byte[106];
        return null;
    }

    public int getReadOffset() {
        return buffReaderHead.read();
    }

    public int getWriteOffset() {
        return buffWriterHead.read();
    }

    /**
     * 强制写入文件
     */
    public void force() {
        if (buffWriterHead != null) {
            buffWriterHead.force();
        }
        if (buffBody != null) {
            buffBody.force();
        }
    }

    public ResultAdderWrite write(byte[] value) {
        try {
            wtlock.lock();
            if (value.length + GlobalDefine.TF_RECORD_LEN_BYTE > pager.getPageSize()) {
                String msg = "TFWriter中，写入的数据超过TF的内存区的大小";
                ATFLogger.syserr(msg);
                throw new TFException(ATFConstants.ERR_ATF_TF_DATA, msg);
            }
            int dataHeadLen = value.length + GlobalDefine.TF_RECORD_LEN_BYTE;
            byte[] len_and_data = new byte[dataHeadLen];
            ByteUtil.writeShort(len_and_data, 0, (short) value.length);
            System.arraycopy(value, 0, len_and_data, GlobalDefine.TF_RECORD_LEN_BYTE, value.length);
            ResultAdderWrite resultAdderWrite = writeToBuffBody(len_and_data);
            if (resultAdderWrite.getCode() == 0) {
                writedTotal++;
            }
            ATFLogger.sysinfo("写入TF文件成功 : " + resultAdderWrite);
            return resultAdderWrite;
        } finally {
            wtlock.unlock();
        }
    }

    /**
     * @param data
     * @return
     */
    private ResultAdderWrite writeToBuffBody(byte[] data) {
        ResultAdderWrite resultAdderWrite = new ResultAdderWrite();
        int len = data.length;
        int posAdder = buffBody.position() + pager.getFirst() - 1;
        parseResultInfo(resultAdderWrite, posAdder, len);

        int pageNo = pager.getPageNo();
        if (posAdder + len > pager.getTotalCount() - GlobalDefine.TF_LEN_END) {
            resultAdderWrite.setCode(GlobalDefine.TF_STATUS_END);
            return resultAdderWrite;
        } else {
            // 如果剩下的内存不够
            if (buffBody.remaining() < len) {
                ATFLogger.sysinfo("剩余内存不足，分两次写入数据");
                splitByteAndChangeBody(data);
            } else {
                // 写入数据
                buffBody.put(data);
            }
        }

        // 增加adder指针
        buffWriterHead.write(posAdder);
        resultAdderWrite.setCode(0);
        return resultAdderWrite;

    }

    private void parseResultInfo(ResultAdderWrite resultAdderWrite, int posAdder, int len) {
        resultAdderWrite.setOffsetBefore(posAdder);
        resultAdderWrite.setOffsetAfter(posAdder + len);
        resultAdderWrite.setMemoryRemain(buffBody.remaining() - len);
        int fileMax = (int) getPager().getTotalCount();
        // 当前页的开始下标
        int currPageFirst = getPager().getFirst() - 1;
        // 当前内存的位置
        int currPosInMemory = getBuffBody().position() + len;
        resultAdderWrite.setFileRemain(fileMax - (currPageFirst + currPosInMemory) - GlobalDefine.TF_LEN_END);
        resultAdderWrite.setOffsetAfter(posAdder + len);
    }

    public void writeTFEnd(String nextFilePath) {
        int posAdder = buffBody.position() + ((pager.getPageNo() - 1) * pager.getPageSize());
        ATFLogger.sysinfo("文件空间已用完，写TF文件结束");
        byte[] end_all = new byte[GlobalDefine.TF_LEN_END];
        byte[] end_data = BaseHeader.getTFEndData(nextFilePath);
        ByteUtil.writeShort(end_all, 0, (short) (GlobalDefine.TF_LEN_END - GlobalDefine.TF_RECORD_LEN_BYTE));
        System.arraycopy(end_data, 0, end_all, 2, end_data.length);
        buffBody.put(end_all);
        buffWriterHead.write(posAdder);
    }

    public void splitByteAndChangeBody(byte[] data) {
        int len = data.length;
        int leftSize = buffBody.remaining();
        // 将数组拆分，获得第一个数组并写入TF
        byte[] b1 = new byte[leftSize];
        System.arraycopy(data, 0, b1, 0, b1.length);
        buffBody.put(b1);
        // 获得第二个数组,映射一个新的内存，并写入TF
        byte[] b2 = new byte[len - leftSize];
        System.arraycopy(data, leftSize, b2, 0, b2.length);

        // 直接跳到下一个内存块的开头
        pager.setPageNo(pager.getPageNo() + 1);
        createTFBody();
        buffBody.put(b2);
    }

    private void createTFBody() {
        try {
            buffBody = channel.map(FileChannel.MapMode.READ_WRITE, GlobalDefine.TF_HEAD_LEN_ALL + pager.getFirst() - 1, pager.getPageSize());
        } catch (IOException e) {
            String msg = "TFWriter中切换内存发生错误 : " + e.toString();
            ATFLogger.syserr(msg);
            throw new TFException(ATFConstants.ERR_ATF_TF_MEM_CHANGE, msg);
        }
    }

    public boolean isTFEnd() {
        int writeOffset = getWriteOffset();
        // System.out.println("writeOffset = "+writeOffset);
        MappedByteBuffer buff = BuffWriterCommon.createReadBuff(channel, GlobalDefine.TF_HEAD_LEN_ALL + getWriteOffset(), 12);
        byte[] data = new byte[12];
        buff.position(0);
        buff.get(data);
        // System.out.println("len = "+len);
        String type = ByteUtil.readString(data, 10, 2);
        // System.out.println("type = "+type);
        return "23".equals(type);
    }

    // 写TF开始
    public byte[] getStartData() {
        return new byte[]{};
    }

    /**
     * 关闭文件池
     */
    public boolean baseClose() {
        try {
            if (!beclosed) {
                beclosed = true;
            }
            if (accfile != null) {
                accfile.close();
            }
            if (channel != null) {
                channel.close();
            }
            return true;
        } catch (Throwable th) {
            return false;
        }
    }

}
